// Copyright (c) 2025 Inochi Amaoto

/* SPDX-License-Identifier: GPL-2.0 */

#ifndef __VIRTIO_VAPP_DEV_H__
#define __VIRTIO_VAPP_DEV_H__

#include "virtio-vapp.h"
#include "virtio-vapp-dtq.h"

static void virtio_vapp_dev_init(struct virtio_vapp_dev* dev, struct virtio_device* vdev, struct virtio_vapp_cpu_config* conf)
{
  size_t i;
  dev->config.uuid = conf->uuid;
  dev->config.multiqueues = conf->multiqueues;

  dev->vdev = vdev;

  vapp_send_queue_init(&dev->cqueue);
  vapp_send_queue_init(&dev->dqueue);

  atomic_set(&dev->id_pool, 1);

  // TODO: dtqs;
  virtio_vapp_dtq_init_ctrl(&dev->cdtq);
  virtio_vapp_dtq_set_queue(&dev->cdtq, &dev->cqueue);
  for (i = 0; i < conf->multiqueues; i++) {
    virtio_vapp_dtq_init_data(&dev->ddtqs[i]);
    virtio_vapp_dtq_set_queue(&dev->ddtqs[i], &dev->dqueue);
  }

  return;
}

static struct virtio_vapp_dev* virtio_vapp_dev_alloc(struct virtio_device* vdev, struct virtio_vapp_cpu_config* conf)
{
  struct virtio_vapp_dev* dev = vmalloc(sizeof(struct virtio_vapp_dev));
  if (!dev)
    return NULL;

  dev->ddtqs = vmalloc(sizeof(struct virtio_vapp_dtq) * conf->multiqueues);
  if (!dev->ddtqs) {
    kvfree(dev);
    return NULL;
  }

  virtio_vapp_dev_init(dev, vdev, conf);
  return dev;
}

static void virtio_vapp_dev_free(struct virtio_vapp_dev* dev)
{
  if (dev->ddtqs)
    kvfree(dev->ddtqs);
  kvfree(dev);
}

static int virtio_vapp_dev_assign_virtqueue(struct virtio_vapp_dev* vapp)
{
  const static char* names[2 * (VIO_VAPP_NR_DATA_DTQS_MAX + VIO_VAPP_NR_CTRL_DTQS_MAX)] = {
    /* ctrl dtq */
    [__VAPP_VQ_RX] = "vapp-ctrl-rx",
    [__VAPP_VQ_TX] = "vapp-ctrl-tx",

    /* data dtqs */
    [__VAPP_VQ_RX + __VAPP_VQ_MAX * 1] = "vapp-data-0-rx",
    [__VAPP_VQ_TX + __VAPP_VQ_MAX * 1] = "vapp-data-0-tx",
    [__VAPP_VQ_RX + __VAPP_VQ_MAX * 2] = "vapp-data-1-rx",
    [__VAPP_VQ_TX + __VAPP_VQ_MAX * 2] = "vapp-data-1-tx",
    [__VAPP_VQ_RX + __VAPP_VQ_MAX * 3] = "vapp-data-2-rx",
    [__VAPP_VQ_TX + __VAPP_VQ_MAX * 3] = "vapp-data-2-tx",
    [__VAPP_VQ_RX + __VAPP_VQ_MAX * 4] = "vapp-data-3-rx",
    [__VAPP_VQ_TX + __VAPP_VQ_MAX * 4] = "vapp-data-3-tx",
  };

  static vq_callback_t* callbacks[] = {
    /* ctrl dtq */
    [__VAPP_VQ_RX] = virtio_vapp_dtq_ctrl_rx_kick,
    [__VAPP_VQ_TX] = virtio_vapp_dtq_ctrl_tx_kick,

    /* data dtqs */
    [__VAPP_VQ_RX + __VAPP_VQ_MAX * 1] = virtio_vapp_dtq_data_rx_kick,
    [__VAPP_VQ_TX + __VAPP_VQ_MAX * 1] = virtio_vapp_dtq_data_tx_kick,
    [__VAPP_VQ_RX + __VAPP_VQ_MAX * 2] = virtio_vapp_dtq_data_rx_kick,
    [__VAPP_VQ_TX + __VAPP_VQ_MAX * 2] = virtio_vapp_dtq_data_tx_kick,
    [__VAPP_VQ_RX + __VAPP_VQ_MAX * 3] = virtio_vapp_dtq_data_rx_kick,
    [__VAPP_VQ_TX + __VAPP_VQ_MAX * 3] = virtio_vapp_dtq_data_tx_kick,
    [__VAPP_VQ_RX + __VAPP_VQ_MAX * 4] = virtio_vapp_dtq_data_rx_kick,
    [__VAPP_VQ_TX + __VAPP_VQ_MAX * 4] = virtio_vapp_dtq_data_tx_kick,
  };

  struct virtqueue* vqs[2 * (VIO_VAPP_NR_DATA_DTQS_MAX + VIO_VAPP_NR_CTRL_DTQS_MAX)];

  int ret = virtio_find_vqs(vapp->vdev, 2 * (vapp->config.multiqueues + 1), vqs, callbacks, names, NULL);
  pr_info("find vqs %d\n", ret);
  if (ret)
    return ret;

  {
    for (size_t i = 0; i < (vapp->config.multiqueues + 1) * 2; ++i)
      pr_info("dev: find vq %p\n", vqs[i]);
  }

  // TODO: assign queues
  virtio_vapp_dtq_assign_virtqueue(&vapp->cdtq, vqs);
  {
    for (size_t i = 0; i < vapp->config.multiqueues; ++i)
      virtio_vapp_dtq_assign_virtqueue(&vapp->ddtqs[i], &vqs[(i + 1) * 2]);
  }
  return 0;
}

#endif // ! __VIRTIO_VAPP_DEV_H__
